package com.fr71.project.dm.listener;

import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class TcpDataWriter {
    //数据包头
    private final int head;
    //是否在get时添加77 90 0 0 0 0 0 0
    private final boolean addBaseHead;
    //是否在get时添加数据总长度标识
    private final boolean addAllSize;
    //是否在get时添加包头
    private final boolean addHead;

    private byte[] data = new byte[0];
    private final byte[] baseHead = new byte[]{77, 90, 0, 0, 0, 0, 0, 0};

    public TcpDataWriter(int head) {
        this(head, true, true, true);
    }

    public TcpDataWriter(
            //数据包头
            int head,
            //是否在get时添加77 90 0 0 0 0 0 0
            boolean addBaseHead,
            //是否在get时添加数据总长度标识
            boolean addAllSize,
            //是否在get时添加包头
            boolean addHead) {
        this.head = head;
        this.addBaseHead = addBaseHead;
        this.addAllSize = addAllSize;
        this.addHead = addHead;
    }

    /**
     * 写入字符串
     * [dataStr] 字符串内容
     * [strMarkSize] 内容长度占用的字节长度 默认用1个字节长度位置标识整个字符串的长度,0标识不添加长度位标识
     */
    public TcpDataWriter write(String dataStr, int strMarkSize) {
        byte[] tmpByteArray = new byte[0];
        byte[] dataByteArray = dataStr.getBytes(StandardCharsets.UTF_8);
        if (strMarkSize > 0) {
            tmpByteArray = toByteArray(dataByteArray.length, strMarkSize);
        }
        tmpByteArray = concat(tmpByteArray, dataByteArray);
        data = concat(data, tmpByteArray);

        return this;
    }

    /**
     * 写入一个数字
     * [size] 该数字占用的字节长度
     */
    public TcpDataWriter write(Number number, int size, boolean reverse) {
        byte[] d = toByteArray(number.longValue(), size);
        if (reverse) {
            byte[] reversed = new byte[d.length];
            for (int i = 0; i < d.length; i++) {
                reversed[i] = d[d.length - 1 - i];
            }
            d = reversed;
        }
        data = concat(data, d);
        return this;
    }

    /**
     * 写入字节数组 直接写入
     */
    public TcpDataWriter write(byte[] byteArray) {
        data = concat(data, byteArray);
        return this;
    }

    /**
     * 写入字节数组 直接写入
     */
    public int writeResultStartIndex(byte[] byteArray) {
        int index = data.length;
        data = concat(data, byteArray);
        return index;
    }

    /**
     * 获取最终写入完成的数据
     */
    public byte[] get() {
        byte[] tmpData = new byte[0];
        tmpData = concat(tmpData, data);
        if (addHead) {
            tmpData = concat(toByteArray(head, 2), tmpData);
        }

        if (addAllSize) {
            tmpData = concat(toByteArray(tmpData.length, 2), tmpData);
        }

        if (addBaseHead) {
            tmpData = concat(baseHead, tmpData);
        }

        return tmpData;
    }

    public static String toHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString().toUpperCase();
    }

    public static byte[] hexToByteArray(String hex) {
        int len = hex.length();
        if (len % 2 != 0) {
            throw new IllegalArgumentException("Hex string must have even number of characters");
        }

        byte[] result = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            char firstDigit = hex.charAt(i);
            char secondDigit = hex.charAt(i + 1);
            int first = Character.digit(firstDigit, 16);
            int second = Character.digit(secondDigit, 16);
            if (first < 0 || second < 0) {
                throw new IllegalArgumentException("Non-hex character in input: " + hex);
            }
            result[i / 2] = (byte) ((first << 4) | second);
        }
        return result;
    }

    public static byte[] toByteArray(long value, int len) {
        byte[] byteArray = new byte[len];
        BigInteger bigValue = BigInteger.valueOf(value);
        for (int i = len - 1; i >= 0; i--) {
            byteArray[i] = bigValue.and(BigInteger.valueOf(0xff)).byteValue();
            bigValue = bigValue.shiftRight(8);
        }
        return byteArray;
    }

    public static long toNumber(byte[] bytes) {
        return new BigInteger(1, bytes).longValue();
    }

    private static byte[] concat(byte[] a, byte[] b) {
        byte[] result = new byte[a.length + b.length];
        System.arraycopy(a, 0, result, 0, a.length);
        System.arraycopy(b, 0, result, a.length, b.length);
        return result;
    }
}